/**
 * @file timer.c
 * @brief T1 Timer Driver
 *
 * @author yemt@witsi.cn
 * @date 2011-08-10
 * @version T1.TIMER.01
 */
#include "regs.h"
#include "timer.h"
#include "ictl.h"
#include "dev.h"
#include "init.h"
#include "config.h"

static const uint32_t tim_clocks[] = {
         CONFIG_TIM0_CLK, CONFIG_TIM1_CLK, CONFIG_TIM2_CLK,
         CONFIG_TIM3_CLK, CONFIG_TIM4_CLK, CONFIG_TIM5_CLK,
};

/************************************************************************
 * Hardware Timer Driver
 ************************************************************************/

#define _TIMER_BASE(n)  ((timer_reg_t *)((T1_TIMER_BASE + ((n) << 5))))
#define _TIMER_MAX 6

static timer_irq_t timer_handler[_TIMER_MAX]; /**< Timers Interrupt Handler */

/**
 * @fn timer_irq_handler
 * @brief Timer Interrupt Handler
 *
 * @return void
 */
static void timer_irq_handler(void)
{
        int i;
        for (i = 0; i < _TIMER_MAX; i ++) {
                if (_TIMER_BASE(i)->TIM_MIS & TIM_INT) {
                        _TIMER_BASE(i)->TIM_CLR = TIM_INT;
                        if (timer_handler[i])
                                timer_handler[i]();
                }
        }
}

/**
 * @fn timers_init
 * @brief Timers Initializer
 *
 * @return void
 */
static int timers_init(void)
{
        request_irq(IRQ_TIMER, timer_irq_handler);
        return 0;
}

arch_initcall(timers_init);

/**
 * @fn timer_hw_init
 * @brief Timer Setup
 *
 * This function does not start the timer
 *
 * @param n             Timer Index, from 0 to 5
 * @param mod           Timer Running Mode
 * @param clock         Timer Counting Clock
 * @param reload        Timer Load Value
 * @param handler       Timer Interrupt Handler
 * @return void
 */
void timer_hw_init(int n, timer_mode_t mod, uint32_t clock,
                uint32_t reload, timer_irq_t handler)
{
        timer_reg_t * reg = _TIMER_BASE(n);
        uint32_t ctrl = 0, tclk;

        reg->TIM_CTL = 0; /* disable Timer */

        if (n == 0) {
                ctrl |= TIM_32BIT_EN; /* Timer0 has 32bits mode */
        }
        tclk = tim_clocks[n];

        if (clock <= (tclk >> 8)) ctrl |= TIM_PRE_256;
        else if (clock <= (tclk >> 4)) ctrl |= TIM_PRE_16;

        if ((mod == TIMER_ONESHOT)) {
                ctrl |= TIM_ONESHOT; /* one-shot mode */
        }
        else if ((mod == TIMER_PERIODIC)) {
                ctrl |= TIM_PERIODIC; /* periodic mode */
        }

        if (handler) {
                timer_handler[n] = handler;
                ctrl |= TIMI_EN; /*  interrupt enable */
        }
        reg->TIM_LOAD = reload;
        reg->TIM_CTL = ctrl;
        return;
}

/**
 * @fn timer_hw_start
 * @brief Timer Start
 *
 * @param n             Timer Index, from 0 to 5
 * @return void
 */
void timer_hw_start(int n)
{
        _TIMER_BASE(n)->TIM_CTL |= TIM_EN | TIMI_EN;
}

/**
 * @fn timer_hw_stop
 * @brief Timer Stop
 *
 * @param n             Timer Index, from 0 to 5
 * @return void
 */
void timer_hw_stop(int n)
{
        _TIMER_BASE(n)->TIM_CTL &= ~(TIM_EN | TIMI_EN);
}

/**
 * @fn timer_hw_value
 * @brief Timer Current Value
 *
 * @param n             Timer Index, from 0 to 5
 * @return
 *      uint32_t        Timer Counter Current Value
 */
uint32_t timer_hw_value(int n)
{
        return _TIMER_BASE(n)->TIM_VAL;
}

/**
 * @fn timer_hw_flag
 * @brief Return the overflow flag
 *
 * @param n             Timer Index, from 0 to 5
 * @return
 *      0:              No Overflow
 *      1:              An Overflow happened
 */
int timer_hw_flag(int n)
{
        return _TIMER_BASE(n)->TIM_RIS;
}

/**
 * @fn timer_hw_flag_clear
 * @brief Clear the Timer Overflow Flag
 *
 * @param n             Timer Index, from 0 to 5
 * @return void
 */
void timer_hw_flag_clear(int n)
{
        _TIMER_BASE(n)->TIM_CLR = 1;
}

/**
 * @fn timer_hw_set_load
 * @brief Set Timer Load Value
 *
 * @param n             Timer Index, from 0 to 5
 * @param load          The new load value
 * @return void
 */
void timer_hw_set_load(int n, uint32_t load)
{
        _TIMER_BASE(n)->TIM_LOAD = load;
}


/**
 * @fn timer_hw_get_load
 * @brief get Timer Load Value
 *
 * @param n             Timer Index, from 0 to 5
 * @param load          The new load value
 * @return void
 */
uint32_t timer_hw_get_load(int n)
{
      return  _TIMER_BASE(n)->TIM_LOAD;
}

/**
 * @fn timer_hw_set_reload
 * @brief Set Timer Re-Load Value
 *
 * @param n             Timer Index, from 0 to 5
 * @param reload        The new reload value
 * @return void
 */
void timer_hw_set_reload(int n, uint32_t reload)
{
        _TIMER_BASE(n)->TIM_BG = reload;
}



